package com.app.module.camera.core.provider

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.hardware.camera2.*
import android.hardware.camera2.params.StreamConfigurationMap
import android.media.MediaActionSound
import android.media.MediaRecorder
import android.os.Handler
import android.os.HandlerThread
import android.util.*
import android.view.Surface
import com.app.module.camera.base.SimpleSize
import com.app.module.camera.ext.*
import com.app.module.camera.ui.Camera2Activity
import com.app.module.camera.utils.TextureUtils
import java.util.*
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit

class CameraProvider : Provider {

    private val MAX_PREVIEW_WIDTH = 1920
    private val MAX_PREVIEW_HEIGHT = 1080
    private val MAX_VIDEO_WIDTH = 4096
    private val MAX_VIDEO_HEIGHT = 2160

    private val mActivity: Camera2Activity

    private var mSensorOrientation = 0
    private var mZoomLevel = 1f
    private var mMaxZoomLevel = 1f
    private var mIsFlashSupported = true
    private var mIsZoomSupported = true
    private var mIsFocusSupported = true
    private var mUseFrontCamera = false
    private var mIsInPhoneMode = true
    private var mCameraId = ""
    private var mCameraState = STATE_INIT

    private var mBackgroundThread: HandlerThread? = null
    private var mBackgroundHandler: Handler? = null

    // 默认预览大小
    private lateinit var mPreviewSize: Size

    private var mCameraDevice: CameraDevice? = null
    private var mCameraCharacteristics: CameraCharacteristics? = null
    private var mCaptureSession: CameraCaptureSession? = null
    private var mPreviewRequestBuilder: CaptureRequest.Builder? = null
    private var mPreviewRequest: CaptureRequest? = null

    private val mCameraOpenCloseLock = Semaphore(1)
    private var mFrameSem: Semaphore? = null

    private val mMediaActionSound = MediaActionSound()

    // 相机预览的回调
    private val mImagePreviewCallback = object : CameraCaptureSession.StateCallback() {
        override fun onConfigured(cameraCaptureSession: CameraCaptureSession) {
            mCaptureSession = cameraCaptureSession
            try {
                // 预览请求
                mPreviewRequestBuilder?.also {
                    it.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, getFrameRange())
                    mPreviewRequest = it.build()
                    cameraCaptureSession.setRepeatingRequest(mPreviewRequest!!, mImageCaptureCallback, mBackgroundHandler)
                }
                mCameraState = STATE_PREVIEW
            } catch (e: Exception) {
            }
        }

        override fun onConfigureFailed(cameraCaptureSession: CameraCaptureSession) {}
    }

    // 相机 拍照一次对话回调
    private val mImageCaptureCallback = object : CameraCaptureSession.CaptureCallback() {
        override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) {
            // TOOD 信号量的释放
            mFrameSem?.drainPermits()
            mFrameSem?.release()
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    constructor(activity: Camera2Activity, initPhotoMode: Boolean) {
        this.mActivity = activity
        val cameraCharacteristics = try {
            getCameraCharacteristics(activity.appConfig.lastUsedCamera)
        } catch (e: Exception) {
            mActivity.toast("Get camera characteristics $e")
            null
        }
        val isFrontCamera = cameraCharacteristics?.get(CameraCharacteristics.LENS_FACING).toString() == activity.frontCameraId.toString()
        mUseFrontCamera = !activity.appConfig.alwaysOpenBackCamera && isFrontCamera
        mIsInPhoneMode = initPhotoMode
        mMediaActionSound.load(MediaActionSound.START_VIDEO_RECORDING)
        mMediaActionSound.load(MediaActionSound.STOP_VIDEO_RECORDING)
        mMediaActionSound.load(MediaActionSound.SHUTTER_CLICK)
        // TODO
        // TODO 手势识别
    }

    private fun onPaused() {
        closeCamera()
        try {
            mBackgroundThread?.quitSafely()
            mBackgroundThread?.join()
            mBackgroundThread = null
            mBackgroundHandler = null
        } catch (e: InterruptedException) {
        }
    }

    fun setOutputSize(width: Int, height: Int) {
        Log.e("Camera2Activity","CameraProvider -> setOutputSize  $width  $height")
        setupCameraOutputs(width ,height)
    }

    @SuppressLint("MissingPermission")
    private fun openCamera(textureSurface: SurfaceTexture) {
        HandlerThread("SimpleCameraBackground").also {
            it.start()
            mBackgroundHandler = Handler(it.looper)
            mBackgroundThread = it
        }
        try {
            // 打开并设置相机的输出大小
            mActivity.runOnUiThread {
                if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
                    throw RuntimeException("Time out waiting to lock camera opening.")
                }
                getCameraManager().openCamera(mCameraId, SimpleStateCallback(textureSurface), mBackgroundHandler)
            }
        } catch (e: Exception) {
            mActivity.toast("Open camera $e")
        }
    }

    private fun closeCamera() {
        try {
            mCameraOpenCloseLock.acquire()
            mCaptureSession?.close()
            mCaptureSession = null
            mCameraDevice?.close()
            mCameraDevice = null
        } catch (e: Exception) {
        } finally {
            mCameraOpenCloseLock.release()
        }
    }

    private fun closeCaptureSession() {
        mCaptureSession?.close()
        mCaptureSession = null
    }

    // 获取当前分辨率
    private fun getCurrentResolution(): SimpleSize {
        val configMap = mCameraCharacteristics?.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
            ?: return SimpleSize(0, 0)
        val resIndex = if (mUseFrontCamera) {
            if (!mIsInPhoneMode) {
                mActivity.appConfig.frontVideoResIndex
            } else {
                mActivity.appConfig.frontPhotoResIndex
            }
        } else {
            if (!mIsInPhoneMode) {
                mActivity.appConfig.backVideoResIndex
            } else {
                mActivity.appConfig.backPhotoResIndex
            }
        }

        val outputSizes = if (!mIsInPhoneMode) {
            getAvailableVideoSizes(configMap)
        } else {
            getAvailableImageSizes(configMap)
        }
        val size = outputSizes.sortedByDescending { it.width * it.height }[resIndex]
        return SimpleSize(size.width, size.height)
    }

    // 设置相机的输出发小
    private fun setupCameraOutputs(width: Int, height: Int) {
        val manager = getCameraManager()
        try {
            for (cameraId in manager.cameraIdList) {
                // 获取每个Camera的详细属性类
                val characteristics = getCameraCharacteristics(cameraId)
                // 相机设备相对于屏幕的方向
                val facing = characteristics.get(CameraCharacteristics.LENS_FACING) ?: continue
                // 根据当前摄像头,检查其方向是否相左
                if ((mUseFrontCamera && facing == CameraCharacteristics.LENS_FACING_BACK)
                    //后置摄像头一般是 LENS_FACING_FRONT
                    || (!mUseFrontCamera && facing == CameraCharacteristics.LENS_FACING_FRONT)) {
                    continue
                }
                //设置id
                mCameraId = cameraId
                mCameraCharacteristics = characteristics
                //active 区域宽高和裁剪区域宽度比值的最大值，用于 android.scaler.cropRegion
                mMaxZoomLevel = mCameraCharacteristics?.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM)
                    ?: return
                mZoomLevel = 1f
                // 设置id
                mActivity.appConfig.lastUsedCamera = mCameraId

                characteristics.apply {
                    //相机设备是否有闪光灯部件
                    mIsFlashSupported = get(CameraCharacteristics.FLASH_INFO_AVAILABLE) ?: false
                    // 是否可放大
                    mIsZoomSupported = get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM) ?: 0f > 0f
                    // 相机设备支持的自动聚焦模式列表
                    mIsFocusSupported = get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES)?.let { it.size > 1  } ?: false

                }

                //相机设备支持的可用流的配置，包括最小帧间隔、不同格式、大小组合的失帧时长
                val configMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!
                // 获取当前分辨率
                val currentResolution: SimpleSize = getCurrentResolution()

                val displaySize = getRealDisplaySize()
                var maxPreviewWidth = displaySize.width
                var maxPreviewHeight = displaySize.height
                var rotatedPreviewWidth = width
                var rotatedPreviewHeight = height
                //使输出图像在设备屏幕上以本机方向垂直，顺时针方向旋转的角度
                mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!!
                if (mSensorOrientation == 90 || mSensorOrientation == 270) {
                    rotatedPreviewWidth = height
                    rotatedPreviewHeight = width

                    val tmpWidth = maxPreviewWidth
                    maxPreviewWidth = maxPreviewHeight
                    maxPreviewHeight = tmpWidth
                }

                if (maxPreviewWidth > MAX_PREVIEW_WIDTH) {
                    maxPreviewWidth = MAX_PREVIEW_WIDTH
                }

                if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) {
                    maxPreviewHeight = MAX_PREVIEW_HEIGHT
                }

                val outputSizes = if (!mIsInPhoneMode) {
                    getAvailableVideoSizes(configMap)
                } else {
                    configMap.getOutputSizes(SurfaceTexture::class.java)
                }
                mPreviewSize = chooseOptimalPreviewSize(outputSizes, rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth, maxPreviewHeight, currentResolution)
                mActivity.setAspectRatio(mPreviewSize.height, mPreviewSize.width)
                return
            }
        } catch (e: Exception) {
            mActivity.toast("Setup camera outputs $e")
        }
    }

    //选择一个合适的预览大小
    private fun chooseOptimalPreviewSize(choices: Array<Size>, textureViewWidth: Int, textureViewHeight: Int, maxWidth: Int, maxHeight: Int, selectedResolution: SimpleSize): Size {
        val bigEnough = ArrayList<Size>()
        val notBigEnough = ArrayList<Size>()
        val bigEnoughIncorrectAR = ArrayList<Size>()
        val notBigEnoughIncorrectAR = ArrayList<Size>()
        val width = selectedResolution.width
        val height = selectedResolution.height
        for (option in choices) {
            if (option.width <= maxWidth && option.height <= maxHeight) {
                if (option.height == option.width * height / width) {
                    if (option.width >= textureViewWidth && option.height >= textureViewHeight) {
                        bigEnough.add(option)
                    } else {
                        notBigEnough.add(option)
                    }
                } else {
                    if (option.width >= textureViewWidth && option.height >= textureViewHeight) {
                        bigEnoughIncorrectAR.add(option)
                    } else {
                        notBigEnoughIncorrectAR.add(option)
                    }
                }
            }
        }

        return when {
            bigEnough.isNotEmpty() -> selectMin(bigEnough) { it.width * it.height }!!
            notBigEnough.isNotEmpty() -> selectMax(notBigEnough) { it.width * it.height }!!
            bigEnoughIncorrectAR.isNotEmpty() -> selectMin(bigEnoughIncorrectAR) { it.width * it.height }!!
            notBigEnoughIncorrectAR.isNotEmpty() -> selectMax(notBigEnoughIncorrectAR) { it.width * it.height }!!
            else -> selectedResolution.toSize()
        }
    }

    private fun selectMin(list: ArrayList<Size>, selector: (Size) -> Int): Size? {
        if (list.isEmpty()) return null
        var minElem = list[0]
        val lastIndex = list.lastIndex
        if (lastIndex == 0) return minElem
        var minValue = selector(minElem)
        for (i in 1..lastIndex) {
            val e = list[i]
            val v = selector(e)
            if (minValue > v) {
                minElem = e
                minValue = v
            }
        }
        return minElem
    }

    private fun selectMax(list: ArrayList<Size>, selector: (Size) -> Int): Size? {
        val iterator = list.iterator()
        if (!iterator.hasNext()) return null
        var maxElem = iterator.next()
        if (!iterator.hasNext()) return maxElem
        var maxValue = selector(maxElem)
        do {
            val e = iterator.next()
            val v = selector(e)
            if (maxValue < v) {
                maxElem = e
                maxValue = v
            }
        } while (iterator.hasNext())
        return maxElem
    }

    private fun getRealDisplaySize(): SimpleSize {
        val metrics = DisplayMetrics()
        mActivity.windowManager.defaultDisplay.getRealMetrics(metrics)
        return SimpleSize(metrics.widthPixels, metrics.heightPixels)
    }

    private fun getFrameRange(): Range<Int> {
        val ranges = mCameraCharacteristics?.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES)
            ?: return Range<Int>(0, 1)
        var currRangeSize = -1
        var currMinRange = 0
        var result: Range<Int>? = null
        for (range in ranges) {
            val diff = range.upper - range.lower
            if (diff > currRangeSize || (diff == currRangeSize && range.lower > currMinRange)) {
                currRangeSize = diff
                currMinRange = range.lower
                result = range
            }
        }

        return result!!
    }

    private fun getCameraManager() = mActivity.getSystemService(Context.CAMERA_SERVICE) as CameraManager

    private fun getCameraCharacteristics(cameraId: String = mCameraId) = getCameraManager().getCameraCharacteristics(cameraId)

    private fun getAvailableVideoSizes(configMap: StreamConfigurationMap) = configMap.getOutputSizes(MediaRecorder::class.java).filter {
        it.width <= MAX_VIDEO_WIDTH && it.height <= MAX_VIDEO_HEIGHT
    }.toTypedArray()

    private fun getAvailableImageSizes(configMap: StreamConfigurationMap, int: Int = ImageFormat.JPEG) = configMap.getOutputSizes(int)

    @SuppressLint("MissingPermission")
    override fun open(): TextureBean {
        this.mFrameSem = Semaphore(0)
        val textureId = TextureUtils.createTextureID(true)
        val textureSurface = SurfaceTexture(textureId)
        textureSurface.setDefaultBufferSize(mPreviewSize.width, mPreviewSize.height)
        Log.e("Camera2Activity","CameraProvider -> open() $mPreviewSize")
        openCamera(textureSurface)
        return TextureBean(textureId, textureSurface, mPreviewSize)
    }

    override fun close() {
        mFrameSem?.drainPermits()
        mFrameSem?.release()
        mFrameSem = null
        onPaused()
    }

    override fun frame(): Boolean {
        try {
            mFrameSem?.acquire()
        } catch (e: InterruptedException) {
            e.printStackTrace()
        }
        return false
    }

    override fun time(): Long {
        return System.currentTimeMillis()
    }

    override fun isLandscape(): Boolean {
        return false
    }

    inner class SimpleStateCallback(private val surface: SurfaceTexture) : CameraDevice.StateCallback() {
        // 打开成功
        @SuppressLint("Recycle")
        override fun onOpened(cameraDevice: CameraDevice) {
            mCameraOpenCloseLock.release()
            mCameraDevice = cameraDevice
            // 设置预览
            try {
                // 释放旧的照相对话
                closeCaptureSession()
                surface.setDefaultBufferSize(mPreviewSize.width, mPreviewSize.height)
                val list = Collections.singletonList(Surface(surface))
                // 新建一个捕获请求
                mPreviewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).apply {
                    set(CaptureRequest.CONTROL_CAPTURE_INTENT, CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW)
                    addTarget(list.get(0))
                }
                cameraDevice.createCaptureSession(list, mImagePreviewCallback, mBackgroundHandler)
            } catch (e: Exception) {

            }
        }

        // 相机断开链接
        override fun onDisconnected(cameraDevice: CameraDevice) {
            mCameraOpenCloseLock.release()
            cameraDevice.close()
            mCameraDevice = null
        }

        // 相机打开失败
        override fun onError(cameraDevice: CameraDevice, error: Int) {
            mCameraOpenCloseLock.release()
            cameraDevice.close()
            mCameraDevice = null
        }
    }

    fun rotateCamera() {

    }

    fun switchCamera() {
        mUseFrontCamera = !mUseFrontCamera
    }

    fun getSensorOrientation() = mSensorOrientation

}
